package com.xxx.filter;

import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;

import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 * Created by louisyuu on 2020/5/5 下午2:09
 */

@Activate(group = {CommonConstants.CONSUMER})
public class TPMonitorFilter implements Filter {

    /**
     * What is TP90 TP99?
     * Answer:TP99就是满足百分之九十九的网络请求所需要的最低耗时
     * The tp90 is a minimum time under which 90% of requests have been served.
     * Imagine you have response times: 10s 1000s 100s 2s
     * Calculating TP is very simple:
     * 1. Sort all times in ascending[[ə'sɛndɪŋ]上升的，增长的] order: [2s, 10s, 100s, 1000s]
     * 2. find latest item in portion you need to calculate.
     * 2.1 For TP50 it will be ceil(4*0.5) = 2 requests. You need 2nd request.
     * 2.2 For TP90 it will be ceil(4*0.9) = 4. You need 4th request.
     * 3. We get time for the item found above. TP50=10s. TP90=1000s
     */


    /**
     * 请求总次数，用来计算2000次请求中的TP90、TP99是多少
     */
    private static final int STATS_STANDARD_REQUESTS = 2000;

    public static final int TP90 = (int) (STATS_STANDARD_REQUESTS * 0.90);

    public static final int TP99 = (int) (STATS_STANDARD_REQUESTS * 0.99);

    public static final String[] TP_NAMES = {"TP90", "TP99", "TP99.9", "TP100"};


    private static LinkedList<TPWrapper> responseElapsedTimes = new LinkedList<>();


    public TPMonitorFilter() {
        System.out.println("=========TPMonitorFilter init=========");


        /**
         * 打印TP90 TP99 当Response elapsed time size>2000的时候开始统计TP90 TP99
         */
        Executors.newSingleThreadScheduledExecutor().scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {


                if (responseElapsedTimes.size() >= STATS_STANDARD_REQUESTS) {


                    /**
                     * 按照消耗时间顺序排序
                     */
                    System.out.println("=========Response elapsed time size: " + responseElapsedTimes.size());


                    System.out.println("=========TP90:" + responseElapsedTimes.get(TP90).elapsed + " ms");

                    System.out.println("=========TP99:" + responseElapsedTimes.get(TP99).elapsed + " ms");


                } else {
                    System.out.println("=========Response elapsed time size: " + responseElapsedTimes.size());
                }


            }
        }, 5000, 5000, TimeUnit.MILLISECONDS);

    }

    @Override

    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {

        long startTime = System.currentTimeMillis();
        try {
            // 执行方法
            return invoker.invoke(invocation);
        } finally {
            long elapsed = (System.currentTimeMillis() - startTime);
            recordElapsedTimes(elapsed);
        }
    }


    private synchronized void recordElapsedTimes(long elapsed) {


        if (responseElapsedTimes.size() == STATS_STANDARD_REQUESTS) {
            /**
             * 超过2000次记录后，按照消耗记录时间顺序排序，删除第一个记录
             */
            responseElapsedTimes.sort(Comparator.comparingLong(TPWrapper::getElapsed));
            responseElapsedTimes.removeFirst();
        }

        responseElapsedTimes.add(new TPWrapper(elapsed, System.currentTimeMillis()));

        /**
         * 按照消耗时间顺序排序
         */
        responseElapsedTimes.sort(Comparator.comparingLong(TPWrapper::getElapsed));

    }


    public static class TPWrapper {

        public long elapsed;

        public long recordTimestamp;

        public TPWrapper(long elapsed, long recordTimestamp) {
            this.elapsed = elapsed;
            this.recordTimestamp = recordTimestamp;
        }

        public long getElapsed() {
            return elapsed;
        }

        public long getRecordTimestamp() {
            return recordTimestamp;
        }

    }


}
